﻿#include "variant.h"
#include "signalslot.h"
#include "threadsafe.h"
#include "eventloop.h"
#include "UniverseFunction.h"

class ConnectionManager::SlotObj{
public:
    ~SlotObj();

	FunctionAny caller;
	ThreadSafe::ThreadSafeBase* context;
	GenericSignal::ConnectType type;
};

class ConnectionManager::SignalData{
public:
    SlotId maxId;
    std::unordered_map<SlotId,SlotObjPtr> SlotMap;

    std::vector<MetaTypeId> ArgumentTypes;

    SignalData();
};

struct ConnectionManager::Private{
    SignalId MaxId;
    std::unordered_map<SignalId,SignalDataPtr> SignalDatas;
    std::mutex m_mutex;
};

ConnectionManager::ConnectionManager():_P(new Private)
{
    _P->MaxId = 1;
}

ConnectionManager *ConnectionManager::Instance()
{
    static ConnectionManager I;
    return & I;
}

ConnectionManager::SignalId ConnectionManager::RegisterSignal(const std::vector<MetaTypeId>& ArgumentTypes)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    SignalId id = _P->MaxId;
    _P->MaxId ++;

    SignalDataPtr data = std::make_shared<SignalData>();
    data->ArgumentTypes = ArgumentTypes;
    _P->SignalDatas[id] = data;
    return id;
}

void ConnectionManager::UnRegisterSignal(ConnectionManager::SignalId id)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    _P->SignalDatas.erase(id);
}

std::vector<MetaTypeId> ConnectionManager::GetArgumentType(ConnectionManager::SignalId id)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    auto s_data = GetSignalData(id);
    if(!s_data){
        return {};
    }

    return s_data->ArgumentTypes;
}

ConnectionManager::ConnectionId ConnectionManager::Bind(ConnectionManager::SignalId id, const FunctionAny &call, ThreadSafe::ThreadSafeBase* context, int type)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
	auto ctype = GenericSignal::ConnectType(type);
    auto s_data = GetSignalData(id);
    if(!s_data){
        return ConnectionId(0,0);
    }
    SlotId s_id = s_data->maxId;
    s_data->maxId ++;
    ConnectionId c_id(id,s_id);
    SlotObjPtr slot = std::make_shared<SlotObj>();
	slot->caller = call;
	slot->type = ctype;
	slot->context = context;
    
    s_data->SlotMap[s_id] = slot;
	
	if (context) {
		context->GetRegistry()->Append(c_id);
	}

    return c_id;
}

void ConnectionManager::UnBind(ConnectionManager::ConnectionId c_id)
{
    SignalId signal_id = std::get<0>(c_id);
    SlotId slotId = std::get<1>(c_id);
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    auto s_data = GetSignalData(signal_id);
    if(!s_data){
        return;
    }
    s_data->SlotMap.erase(slotId);
}

void ConnectionManager::Invoke(ConnectionManager::SignalId id,const void **param)
{
    std::vector<SlotId> slotObjs;
    SignalData* data;
    {
        std::lock_guard<std::mutex> lock(_P->m_mutex);
        data = GetSignalData(id);
        if(!data){
            return ;
        }

        for(auto i:data->SlotMap){
            slotObjs.push_back(i.first);
        }
    }

    for(auto i:slotObjs){
        SlotObjPtr slotObj;
        {
            std::lock_guard<std::mutex> lock(_P->m_mutex);
            slotObj = data->SlotMap.at(i);
        }

		bool isDirect = true;

		if (slotObj->type == GenericSignal::Auto) {
			if (slotObj->context) {
				if (slotObj->context->thread() == std::this_thread::get_id()) {
					isDirect = true;
				}
				else {
					isDirect = false;
				}
			}
			else
			{
				isDirect = true;
			}
		}
		else if (slotObj->type == GenericSignal::Direct) {
			isDirect = true;
		}
		else if (slotObj->type == GenericSignal::Queue) {
			if (slotObj->context) {
				isDirect = false;
			}
			else {
				isDirect = true;
			}
		}

        if(isDirect){
			//直接执行
			slotObj->caller(NULL,param,NULL);
        }
        else{
			//进行异步回调前要进行参数暂存
			auto argTypes = data->ArgumentTypes;

			typedef std::shared_ptr<std::vector<std::shared_ptr<void>>> ParamPack;
			ParamPack pack = std::make_shared<std::vector<std::shared_ptr<void>>>();
			for (int i = 0; i < argTypes.size(); i++) {
				MetaTypeId type = argTypes[i];
				MetaType meta(type);

				if (!meta.isValid()) {
					std::cerr << "Warning Cannot Queue Param,Type Not Registered!" << std::endl;
					return;
				}

				std::shared_ptr<void> p(meta.Create(NULL, param[i]), [=](void* v) {
					MetaType meta(type);
					meta.Delete(v);
				});
				pack->push_back(p);
			}

			EventLoop::PostEvent(slotObj->context, new ThreadSafe::InvokeFunctionEvent([=]() {
				const void *param[REX_MAX_PARAM_COUNT];

				for (int i = 0; i < pack->size(); i++) {
					param[i] = pack->operator[](i).get();
				}

				slotObj->caller(NULL,param,NULL);
			}));
        }
    }
}

ConnectionManager::SignalData *ConnectionManager::GetSignalData(ConnectionManager::SignalId id)
{
    if(_P->SignalDatas.count(id)){
        return _P->SignalDatas[id].get();
    }

    return NULL;
}

GenericSignal::GenericSignal(std::vector<MetaTypeId> senderType)
{
    SignalId = ConnectionManager::Instance()->RegisterSignal(senderType);
}

GenericSignal::~GenericSignal()
{
    ConnectionManager::Instance()->UnRegisterSignal(SignalId);
}

std::vector<MetaTypeId> GenericSignal::GetArgumentType()
{
    return ConnectionManager::Instance()->GetArgumentType(SignalId);
}

ConnectionManager::ConnectionId GenericSignal::DynamicBind(const std::function<void(const VariantList& param)>& fun, ThreadSafe::ThreadSafeBase* context, ConnectType type)
{
	auto types = GetArgumentType();
	FunctionAny warped = [=](void* o,const void** param,void* ret) {
		
		VariantList p;

		for (int i = 0; i < types.size(); i++) {
			p.push_back(Variant(param[i], types[i]));
		}
		fun(p);
	};

	return DoBind(warped, context,type);
}

ConnectionManager::ConnectionId GenericSignal::UnSafeBind(const std::function<void(const void**param)>& fun, ThreadSafe::ThreadSafeBase* context, ConnectType type)
{
	auto warped = [=](void* o,const void**param,void* ret) {
		fun(param);
	};
	return DoBind(warped, context,type);
}

void GenericSignal::DynamicInvoke(const VariantList &param_)
{
	auto param = param_;
	const void* p[REX_MAX_PARAM_COUNT];
	auto types = GetArgumentType();
	for (int i = 0;i<param.size();i++)
	{	
		p[i] = Variant::BeReadyForParam(param[i], types[i]);
		if (!p[i]) {
			return;
		}
	}
	DoInvoke(p);
}

void GenericSignal::UnsafeInvoke(const void **param)
{
    ConnectionManager::Instance()->Invoke(SignalId,param);
}

void GenericSignal::UnBind(ConnectionManager::ConnectionId id)
{
	ConnectionManager::Instance()->UnBind(id);
}

ConnectionManager::ConnectionId GenericSignal::DoBind(const FunctionAny &fun, ThreadSafe::ThreadSafeBase* context, ConnectType type)
{
    return ConnectionManager::Instance()->Bind(SignalId, fun,context,type);
}

void GenericSignal::DoInvoke(const void **param)
{
    ConnectionManager::Instance()->Invoke(SignalId,param);
}

ConnectionManager::SignalData::SignalData()
{
    maxId = 1;
}

ConnectionManager::SlotObj::~SlotObj()
{
}

int hash(const ConnectionManager::ConnectionId &id)
{
    std::hash<unsigned int> h;
    return h(std::get<0>(id) ) + h(std::get<1>(id));
}

ConnectionRegistry::ConnectionRegistry()
{

}

ConnectionRegistry::~ConnectionRegistry()
{
    auto d = std::move(data);
    for(auto i:d){
        ConnectionManager::Instance()->UnBind(i);
    }
}

void ConnectionRegistry::Append(ConnectionManager::ConnectionId connection)
{
    data.insert(connection);
}

void ConnectionRegistry::operator+=(ConnectionManager::ConnectionId connection)
{
	Append(connection);
}
